<?php
/**
 * Created by PhpStorm.
 * User: worry
 * Date: 2016/12/18
 * Time: 9:08
 */

namespace common\services;

use common\components\Error;
use common\helps\Func;
use Yii;
use yii\base\Object;
use yii\data\Pagination;
use yii\db\Query;
use yii\db\QueryBuilder;
use yii\helpers\ArrayHelper;
use yii\helpers\Url;

/**
 * Class BasicService
 * @property \yii\db\ActiveRecord $errorHandler The error handler application component. This property is read-only.
 * @package app\services
 */
class BasicService extends Object
{
    const CREATE = 1;
    const UPDATE = 2;
    /**
     * @var \yii\db\ActiveRecord 模型类
     */
    protected $model = null; //默认模型
    protected $modelNameSpace = ''; //默认模型命名空间
    protected $attributes = [];
    protected $table = '';//表名

    public $searchField = '';

    /**
     * @var \yii\db\ActiveRecord 模型类
     */
    protected $model_class = '';

    public function init()
    {
        $this->setModelNameSpace();
        parent::init();
    }

    /**
     * 设置默认模型命名空间
     * @return $this
     */
    public function setModelNameSpace()
    {
        if(!$this->model_class) {
            $tmp = explode('\\', self::className());
            $this->table = Func::humpToUnderLine($this->getModelName());
            $this->modelNameSpace = array_shift($tmp) . '\\models';
            $modelClass = $this->modelNameSpace . '\\' . $this->getModelName();
            $this->model_class = $modelClass;
        }
        $modelClass = $this->model_class;
        if (class_exists($modelClass)) {
            $this->model = new $modelClass();
            $this->attributes = $this->model->attributes();
        }
        return $this;
    }

    /**
     * 获取以当前名为基础的Model
     * @return mixed|null
     */
    public function getModelName()
    {
        if ($this->model_class) {
            return array_pop(explode('\\', $this->model_class));
        } else {
            $model = str_replace('Service', '', str_replace('\\', '', strrchr(get_class($this), '\\')));
            return $model;
        }
    }

    /**
     * 查询实例
     * @param null $condition
     * @return \yii\db\ActiveQuery|static|\yii\db\ActiveRecord
     */
    public function findModel($condition = null)
    {
        $modelClass = $this->model_class;

        return $condition ? $modelClass::findOne($condition) : $modelClass::find();
    }

    /**
     * 默认查询分页列表
     * @param array $map
     * @param string $sort
     * @param int $page
     * @param int $pageSize
     * @return array
     * @internal param string $table
     */
    public function getList($map = [], $sort = '', $page = 0, $pageSize = 0)
    {
        $model = $this->findModel()->where($map);
        $count = $model->count();
        $config = ['totalCount' => $count];
        if ($page) {
            $config['page'] = $page;
        }

        $pagination = new Pagination($config);
        if ($pageSize) {
            $pagination->pageSize = $pageSize;
        }
        $list = $model->orderBy($sort)
            ->offset($pagination->offset)
            ->limit($pagination->limit)
            ->asArray()->all();
        return [$list, $pagination];
    }

    /**
     * 默认获取表格数据
     * @param $request
     * @return array
     */
    public function getTableData($request)
    {
        $col = $request['col'];
        $model = $this->findModel();
        //返回信息
        $res = ['draw' => intval($request['draw'])];
        //未过滤总记录
        $res['recordsTotal'] = $model->count();
        $map = [];
        $map[] = 'and';
        //搜索条件
        if ($request['search']['value'] && $this->searchField) {
            $map[] = ['like', $this->searchField, $request['search']['value']];
        }

        //获取过滤条件
        $map = $this->_filterDataTable($map);
        $model = $model->where($map);
        //过滤之后的总记录
        $res['recordsFiltered'] = $model->count();

        //分页限制输出
        $start = ArrayHelper::getValue($request, 'start', 0);
        $length = ArrayHelper::getValue($request, 'length', 10);

        //排序 ['id' => SORT_ASC, 'name' => SORT_DESC]
        $order = [];
        if ($orderTmp = ArrayHelper::getValue($request, 'order')) {
            foreach ($orderTmp as $key => $tmp) {
                $k = array_search($tmp['column'], array_keys($col));
                if ($k !== false) {
                    switch ($tmp['dir']) {
                        case 'desc':
                            $order[$col[$k]] = SORT_DESC;
                            break;
                        case 'asc':
                            $order[$col[$k]] = SORT_ASC;
                            break;
                    }
                }
            }
        }
        //查询字段 优先处理getTableField方法
        $field = method_exists($this, 'getTableField')
            ? $this->getTableField()
            : array_filter($col, function ($value) {
                return in_array($value, $this->attributes);
            });

        //获取数据
        $list = $model//(new Query())->from($this->table)
        ->limit($length)->offset($start)
            ->orderBy($order)->select($field)
            ->all();

        //处理返回数据
        $res['data'] = [];
        foreach ($list as $item) {
            $tmp = [];
            //如果当前service有处理数据方法的话，优先处理
            if (method_exists($this, 'tableHanding')) {
                $tmp = $this->tableHanding($item, $col);
            } else {
                //没有则直接按顺序直接返回数据
                foreach ($col as $field) {
                    if (isset($item[$field])) {
                        $tmp[] = $item[$field];
                    } else if ($field == '_') {
                        $tmp[] = $this->getTableActions($item);
                    } else {
                        $tmp[] = '';
                    }
                }
            }
            if ($tmp) {
                $res['data'][] = $tmp;
            }
        }
        return $res;
    }

    /**
     * 表格操作设置
     * @param $item
     * @return string
     */
    public function getTableActions($item)
    {
        $action = '<div class="btn-group">';
        $pk = $this->model->primaryKey()[0];
        $action .= "<a href=\"" . Url::toRoute(['edit', 'id' => $item[$pk]]) . "\" class=\"btn btn-info\">编辑</a>";
        $action .= "<a data-url=\"" . Url::toRoute(['del', 'id' => $item[$pk]]) . "\" class=\"btn btn-danger ajax-click\">删除</a>";
        $action .= '</div>';
        return $action;
    }

    /**
     * DataTable过滤条件设置
     * @param array $map
     * @return array
     */
    public function _filterDataTable($map = [])
    {
        return $map;
    }

    /**
     * 默认查询所有列表
     * @param array $map
     * @param string $sort
     * @return mixed
     */
    public function getAllList($map = [], $sort = '')
    {
        return $this->findModel()->where($map)->orderBy($sort)->asArray()->all();
    }

    /**
     * 默认查询主键
     * @param $id
     * @param bool $model
     * @return mixed
     */
    public function getInfoByPk($id, $model = false)
    {
        $model_class = $this->model_class;
        $info = $model_class::findOne($id);
        return $model && $info ? $info->toArray() : $info;
    }


    /**
     * 通用保存数据
     * @param $data
     * @return bool
     * @throws \Exception
     */
    public function save($data)
    {
        $model = $this->model;
        $primary_key = $model->primaryKey()[0];
        //保存前数据处理
        if (method_exists($this, 'beforeSave')) {

            $space_arr = explode('\\', $model->className());
            $model_name = array_pop($space_arr);
            $model_data = $data[$model_name];
            //主键判断是否保存或插入
            $mode = isset($model_data[$primary_key]) && $model_data[$primary_key] ? self::UPDATE : self::CREATE;
            $oldData = [];
            if ($mode == self::UPDATE) {
                $model = $this->findModel($model_data[$primary_key]);
                $oldData = $model ? $model->toArray() :  [];
            }
            $this->beforeSave($model_data, $mode, $oldData);
            $data[$model_name] = $model_data;
        }
        $model->load($data);

        if (!$rs = $model->validate()) {
            $error = self::getError($model);
            Error::exception('保存错误:' . $error, 'FAILED');
        }
        $rs = $model->save();
        if ($rs === false) {
            $errorInfoArr = $model->getFirstErrors();
            $errorInfo = array_shift($errorInfoArr);
            Error::exception($errorInfo ? $errorInfo : '保存失败', 'FAILED');
        } else {
            return true;
        }
    }

    /**
     * 获取是否验证错误
     * @param  \yii\db\ActiveRecord $model 验证的模型
     * @return string|null
     */
    public function getError($model)
    {
        $error = $model->getFirstErrors();
        return array_shift($error);
    }

    /**
     * 保存之前的操作
     * @param $data
     * @param int $mode
     * @return bool
     */
    public function beforeSave(&$data, $mode = self::CREATE, $oldData = [])
    {
        return true;
    }

    /**
     * 公用删除
     * @param $id
     * @return bool
     * @throws \Exception
     */
    public function del($id)
    {

        $modelClass = $this->model_class;
        if (is_array($id) && $id) {
            $rs = $modelClass::deleteAll(['id' => $id]);//find()->where()->all()
        } else if ($id) {
            $model = $modelClass::findOne($id);
            if (!$model) {
                throw new \Exception('已被删除');
            }
            $rs = $model->delete();
        } else {
            throw new \Exception('参数异常');
        }
        if ($rs) {
            return true;
        } else {
            throw new \Exception('删除失败');
        }
    }

    /**
     * 返回信息
     * @param string $msg 消息
     * @param bool $status 状态
     * @param array $data 数据
     * @return array 返回结果为数组
     */
    public function returnMsg($msg = '', $status = 'SUCCESS', $data = [])
    {
        return ['status' => $status, 'msg' => $msg, 'data' => $data];
    }

    /**
     * 返回信息数组
     * @param array $return
     * @return array
     */
    public function returnMsgArray($return = [])
    {
        return $return;
    }

    /**
     * @param \yii\db\ActiveRecord $model_class
     * @return BasicService
     */
    public function setModelClass($model_class)
    {
        $this->model_class = $model_class;
        return $this;
    }

    /**
     * @return \yii\db\ActiveRecord
     */
    public function getModelClass()
    {
        return $this->model_class;
    }

    /**
     * 插入到指定数组位置
     * @param $val
     * @param array $array
     * @param $key
     * @return array
     */
    public function prependToArray($val, &$array = [], $key = 0)
    {
        $temp = [];
        for ($i = 0; $i <= count($array); $i++) {
            if (isset($array[$i])) {
                if ($i != $key) {
                    $temp[] = $array[$i];
                } else {
                    $temp[] = $val;
                    $temp[] = $array[$i];
                }
            }
        }

        if (count($temp) == count($array)) {
            $temp[] = $val;
        }
        return $array = $temp;
    }

}